home *** CD-ROM | disk | FTP | other *** search
/ Shareware Grab Bag / Shareware Grab Bag.iso / 011 / tsr.arc / RELEASE.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1986-05-22  |  17.3 KB  |  500 lines

  1. {**************************************************************************
  2. *   Releases memory above the last MARK call made.                        *
  3. *   Copyright (c) 1986 Kim Kokkonen, TurboPower Software.                 *
  4. *   Released to the public domain for personal, non-commercial use only.  *
  5. ***************************************************************************
  6. *   Version 1.0 2/8/86                                                    *
  7. *     original public release.                                            *
  8. *     (thanks to Neil Rubenking for an outline of the method used)        *
  9. *   Version 1.1 2/11/86                                                   *
  10. *     fixed problem with processes which deallocate their environment.    *
  11. *   Version 1.2 2/13/86                                                   *
  12. *     fixed another problem with processes which deallocate environment.  *
  13. *   Version 1.3 2/15/86                                                   *
  14. *     added support for "named" marks.                                    *
  15. *   Version 1.4 2/23/86                                                   *
  16. *     added support for releasing programs which use Expanded Memory.     *
  17. *   Version 1.5 2/28/86                                                   *
  18. *     added more bulletproof method of finding first allocation block.    *
  19. *   Version 1.6 3/20/86                                                   *
  20. *     restore all FF interrupts.                                          *
  21. *     restore the termination address to the local process.               *
  22. *     reduce number of EMS blocks to 32.                                  *
  23. *     fix bug in number of EMS handles in EMS release step.               *
  24. *     restore a mysterious address in the PSP which allows RELEASE of a   *
  25. *       COMMAND shell (emulates the EXIT command).                        *
  26. *   Version 1.7 (date not recorded)                                       *
  27. *     add "protected marks".                                              *
  28. *   Version 1.8 4/21/86                                                   *
  29. *     fix problem when mark is installed as 'MARK '.                      *
  30. *   Version 1.9 5/22/86                                                   *
  31. *     release the environment of MARK when it is not contiguous with      *
  32. *       the MARK itself.                                                  *
  33. *     capture RELEASE calls from within batch files and don't release the *
  34. *       batch file allocation block.                                      *
  35. *     fiddle with different methods of restoring interrupt vectors in     *
  36. *       an attempt to successfully remove DoubleDos. No success, not      *
  37. *       implemented (yet).                                                *
  38. *                                                                         *
  39. ***************************************************************************
  40. *   telephone: 408-378-3672, CompuServe: 72457,2131.                      *
  41. *   requires Turbo version 3 to compile.                                  *
  42. *   Compile with mAx dynamic memory = FFFF.                               *
  43. ***************************************************************************}
  44.  
  45. {$P128}
  46. {$C-}
  47.  
  48. program ReleaseTSR;
  49.   {-release system memory above the last mark call}
  50.   {-release expanded memory blocks allocated since the last mark call}
  51.  
  52. const
  53.   Version = '1.9';
  54.   ProtectChar = '!';          {marks whose name begins with this will be
  55.                               released ONLY if an exact name match occurs}
  56.   MaxBlocks = 128;            {max number of DOS allocation blocks supported}
  57.   MaxHandles = 32;            {max number of EMS allocation blocks supported}
  58.   EMSinterrupt = $67;         {the vector used by the expanded memory manager}
  59.  
  60.   markID = 'MARK PARAMETER BLOCK FOLLOWS'; {marking string for TSR MARK}
  61.  
  62.   {offsets into resident copy of MARK.COM for data storage}
  63.   markOffset = $103;          {where markID is found in TSR}
  64.   vectorOffset = $120;        {where vector table is stored}
  65.   EMScntOffset = $520;        {where count of EMS active pages is stored}
  66.   EMSmapOffset = $522;        {where the page map is stored}
  67.  
  68.   debug = false;              {set true for detailed output report}
  69.  
  70. type
  71.   registers =
  72.   record
  73.     case Integer of
  74.       1 : (ax, bx, cx, dx, bp, si, di, ds, es, flags : Integer);
  75.       2 : (al, ah, bl, bh, cl, ch, dl, dh : Byte);
  76.   end;
  77.  
  78.   HandlePageRecord =
  79.   record
  80.     handle : Integer;
  81.     numpages : Integer;
  82.   end;
  83.  
  84.   PageArray = array[1..MaxHandles] of HandlePageRecord;
  85.   PageArrayPtr = ^PageArray;
  86.  
  87.   Block =
  88.   record                      {store info about each memory block}
  89.     mcb : Integer;
  90.     psp : Integer;
  91.     releaseIt : Boolean;
  92.   end;
  93.  
  94.   BlockType = 0..MaxBlocks;
  95.   BlockArray = array[BlockType] of Block;
  96.   AllStrings = string[255];
  97.   HexString = string[4];
  98.  
  99. var
  100.   Blocks : BlockArray;
  101.   bottomBlock, blockNum : BlockType;
  102.   markName : AllStrings;
  103.   Regs : registers;
  104.   ReturnCode, StartMCB, StoredHandles, EMShandles : Integer;
  105.   Map, StoredMap : PageArrayPtr;
  106.   TrappedBytes : Real;
  107.  
  108.   procedure FindTheBlocks;
  109.     {-scan memory for the allocated memory blocks}
  110.   const
  111.     MidBlockID = $4D;         {byte DOS uses to identify part of MCB chain}
  112.     EndBlockID = $5A;         {byte DOS uses to identify last block of MCB chain}
  113.   var
  114.     mcbSeg : Integer;         {segment address of current MCB}
  115.     nextSeg : Integer;        {computed segment address for the next MCB}
  116.     gotFirst : Boolean;       {true after first MCB is found}
  117.     gotLast : Boolean;        {true after last MCB is found}
  118.     idbyte : Byte;            {byte that DOS uses to identify an MCB}
  119.  
  120.     function GetStartMCB : Integer;
  121.       {-return the first MCB segment}
  122.     begin
  123.       Regs.ah := $52;
  124.       MsDos(Regs);
  125.       GetStartMCB := MemW[Regs.es:(Regs.bx-2)];
  126.     end {getstartmcb} ;
  127.  
  128.     procedure StoreTheBlock(var mcbSeg, nextSeg : Integer;
  129.                             var gotFirst, gotLast : Boolean);
  130.       {-store information regarding the memory block}
  131.     var
  132.       nextID : Byte;
  133.       pspAdd : Integer;       {segment address of the current PSP}
  134.       mcbLen : Integer;       {size of the current memory block in paragraphs}
  135.  
  136.     begin
  137.  
  138.       mcbLen := MemW[mcbSeg:3]; {size of the MCB in paragraphs}
  139.       nextSeg := Succ(mcbSeg+mcbLen); {where the next MCB should be}
  140.       pspAdd := MemW[mcbSeg:1]; {address of program segment prefix for MCB}
  141.       nextID := Mem[nextSeg:0];
  142.  
  143.       if gotLast or (nextID = EndBlockID) or (nextID = MidBlockID) then begin
  144.         blockNum := Succ(blockNum);
  145.         gotFirst := True;
  146.         with Blocks[blockNum] do begin
  147.           mcb := mcbSeg;
  148.           psp := pspAdd;
  149.         end;
  150.       end;
  151.  
  152.     end {storetheblock} ;
  153.  
  154.   begin
  155.  
  156.     {initialize}
  157.     StartMCB := GetStartMCB;
  158.     mcbSeg := StartMCB;
  159.     gotFirst := False;
  160.     gotLast := False;
  161.     blockNum := 0;
  162.  
  163.     {scan all memory until the last block is found}
  164.     repeat
  165.       idbyte := Mem[mcbSeg:0];
  166.       if idbyte = MidBlockID then begin
  167.         StoreTheBlock(mcbSeg, nextSeg, gotFirst, gotLast);
  168.         if gotFirst then mcbSeg := nextSeg else mcbSeg := Succ(mcbSeg);
  169.       end else if gotFirst and (idbyte = EndBlockID) then begin
  170.         gotLast := True;
  171.         StoreTheBlock(mcbSeg, nextSeg, gotFirst, gotLast);
  172.       end else begin
  173.         {start block was invalid}
  174.         WriteLn('Corrupted allocation chain or program error....');
  175.         Halt(1);
  176.       end;
  177.     until gotLast;
  178.  
  179.   end {findtheblocks} ;
  180.  
  181.   function StUpcase(s : AllStrings) : AllStrings;
  182.     {-return the uppercase string}
  183.   var
  184.     i : Byte;
  185.   begin
  186.     for i := 1 to Length(s) do s[i] := UpCase(s[i]);
  187.     StUpcase := s;
  188.   end {stupcase} ;
  189.  
  190.   function FindMark(idString, markName : AllStrings;
  191.                     idOffset : Integer) : Integer;
  192.     {-find the last memory block matching idstring at offset idoffset}
  193.   var
  194.     b : BlockType;
  195.     FoundIt : Boolean;
  196.  
  197.     function MatchString(segment : Integer;
  198.                          idString, markName : AllStrings;
  199.                          idOffset : Integer;
  200.                          var b : BlockType) : Boolean;
  201.       {-return true if idstring is found at segment:idoffset}
  202.     var
  203.       tString : AllStrings;
  204.       len : Byte;
  205.       FoundIt : Boolean;
  206.  
  207.     begin
  208.       len := Length(idString);
  209.       tString[0] := Chr(len);
  210.       Move(Mem[segment:idOffset], tString[1], len);
  211.       FoundIt := (tString = idString);
  212.       if FoundIt then begin
  213.         {check the mark name stored in the PSP of the mark block}
  214.         Move(Mem[segment:$80], tString[0], 128);
  215.         while (Length(tString) > 0) and ((tString[1] = ' ') or (tString[1] = ^I)) do
  216.           Delete(tString, 1, 1);
  217.         if (markName <> '') then begin
  218.           FoundIt := (StUpcase(tString) = StUpcase(markName));
  219.           if not(FoundIt) then
  220.             if (Length(tString) > 0) and (tString[1] = ProtectChar) then
  221.               {current mark is protected, stop searching}
  222.               b := 1;
  223.         end else if (Length(tString) > 0) and (tString[1] = ProtectChar) then begin
  224.           {stored mark name is protected}
  225.           FoundIt := False;
  226.           b := 1;             {stop checking any more}
  227.         end {else match any mark} ;
  228.       end;
  229.       if not(FoundIt) then b := Pred(b);
  230.       MatchString := FoundIt;
  231.     end {matchstring} ;
  232.  
  233.   begin
  234.     {scan from the last block-1 down to find the last MARK TSR}
  235.     b := Pred(blockNum);
  236.     repeat
  237.       FoundIt := MatchString(Blocks[b].psp, idString, markName, idOffset, b);
  238.     until (b < 1) or FoundIt;
  239.     if not(FoundIt) then begin
  240.       WriteLn('No matching marker found, or protected marker encountered.');
  241.       Halt(1);
  242.     end;
  243.     FindMark := b;
  244.   end {findmark} ;
  245.  
  246.   function Hex(i : Integer) : HexString;
  247.     {-return hex representation of integer}
  248.   const
  249.     hc : array[0..15] of Char = '0123456789ABCDEF';
  250.   var
  251.     l, h : Byte;
  252.   begin
  253.     l := Lo(i); h := Hi(i);
  254.     Hex := hc[h shr 4]+hc[h and $F]+hc[l shr 4]+hc[l and $F];
  255.   end {hex} ;
  256.  
  257.   procedure CopyVectors(bottomBlock : BlockType; vectorOffset : Integer);
  258.     {-put interrupt vectors back into table}
  259.   begin
  260.     {interrupts off}
  261.     inline($FA);
  262.     {restore the main interrupt vector table}
  263.     move(Mem[Blocks[bottomBlock].psp:vectorOffset], Mem[0:0], 512);
  264.     {move the old termination/break/error addresses into this program}
  265.     Move(Mem[0:$88], Mem[CSeg:$0A], 6);
  266.     {restore a mysterious address used by the DOS EXIT command to remove a shell}
  267.     move(Mem[CSeg:$0C], Mem[CSeg:$16], 1);
  268.     {interrupts on}
  269.     inline($FB);
  270.   end {copyvectors} ;
  271.  
  272.   procedure MarkBlocks(bottomBlock : BlockType);
  273.     {-mark those blocks to be released}
  274.   var
  275.     b, t : BlockType;
  276.     commandPsp, markPsp : Integer;
  277.  
  278.     function cardinal(i : Integer) : Real;
  279.       {-return "unsigned integer" in range 0..65535}
  280.     var
  281.       r : Real;
  282.     begin
  283.       r := i;
  284.       if r < 0.0 then r := r+65536.0;
  285.       cardinal := r;
  286.     end {cardinal} ;
  287.  
  288.   begin
  289.  
  290.     commandPsp := Blocks[2].psp;
  291.     markPsp := Blocks[bottomBlock].psp;
  292.  
  293.     for b := 1 to blockNum do with Blocks[b] do begin
  294.  
  295.       if (b < bottomBlock) then
  296.         {release the environment of the mark}
  297.         releaseIt := (psp = markPsp)
  298.       else begin
  299.         {release all but RELEASE itself and any blocks owned by COMMAND.COM}
  300.         releaseIt := (psp <> CSeg) and (psp <> commandPsp);
  301.  
  302.         if (psp = commandPsp) then begin
  303.           {warn about the trapping effect of batch files}
  304.           WriteLn('Memory space for TSRs installed prior to batch file');
  305.           WriteLn('will not be released until batch file completes.');
  306.           WriteLn;
  307.           ReturnCode := 1;
  308.           {compute number of bytes temporarily trapped}
  309.           TrappedBytes := 0.0;
  310.           for t := 1 to b do if Blocks[t].releaseIt then
  311.             TrappedBytes := TrappedBytes+16.0*cardinal(MemW[Blocks[t].mcb:3]);
  312.         end;
  313.       end;
  314.     end;
  315.  
  316.     if debug then
  317.       for b := 1 to blockNum do with Blocks[b] do
  318.         WriteLn(b:3, ' ', Hex(mcb), ' ', Hex(psp), ' ', releaseIt);
  319.  
  320.   end {markblocks} ;
  321.  
  322.   procedure ReleaseMem;
  323.     {release DOS memory marked for release}
  324.   var
  325.     b : BlockType;
  326.   begin
  327.     with Regs do
  328.       for b := 1 to blockNum do with Blocks[b] do
  329.         if releaseIt then begin
  330.           ah := $49;
  331.           {the block is always 1 paragraph above the MCB}
  332.           es := Succ(mcb);
  333.           MsDos(Regs);
  334.           if Odd(flags) then begin
  335.             WriteLn('Could not release block at segment ', Hex(es));
  336.             WriteLn('Memory is now a mess... Please reboot');
  337.             Halt(1);
  338.           end;
  339.         end;
  340.   end {releasemem} ;
  341.  
  342.   function EMSpresent : Boolean;
  343.     {-return true if EMS memory manager is present}
  344.   var
  345.     f : file;
  346.   begin
  347.     {"file handle" defined by the expanded memory manager at installation}
  348.     Assign(f, 'EMMXXXX0');
  349.     {$I-} Reset(f) {$I+} ;
  350.     EMSpresent := (IOResult = 0);
  351.     Close(f);
  352.   end {EMSpresent} ;
  353.  
  354.   function EMShandlesActive : Integer;
  355.     {-return the number of active EMS handles}
  356.   begin
  357.     Regs.ah := $4B;
  358.     Intr(EMSinterrupt, Regs);
  359.     if Regs.ah <> 0 then begin
  360.       WriteLn('EMS device not responding');
  361.       EMShandlesActive := 0;
  362.       Exit;
  363.     end;
  364.     EMShandlesActive := Regs.bx;
  365.   end {EMShandlesActive} ;
  366.  
  367.   function GetHandles(bottomBlock : BlockType; EMScntOffset : Integer) : Integer;
  368.     {-return the number of handles stored by mark}
  369.   var
  370.     gh : Integer;
  371.   begin
  372.     Move(Mem[Blocks[bottomBlock].psp:EMScntOffset], gh, 2);
  373.     GetHandles := gh;
  374.   end {gethandles} ;
  375.  
  376.   procedure EMSpageMap(var PageMap : PageArray);
  377.     {-return an array of the allocated memory blocks}
  378.   begin
  379.     Regs.ah := $4D;
  380.     Regs.es := Seg(PageMap);
  381.     Regs.di := Ofs(PageMap);
  382.     Regs.bx := 0;
  383.     Intr(EMSinterrupt, Regs);
  384.     if Regs.ah <> 0 then
  385.       WriteLn('EMS device not responding');
  386.   end {EMSpageMap} ;
  387.  
  388.   procedure ReleaseEMSblocks(var oldmap, newmap : PageArray);
  389.     {-release those EMS blocks allocated since MARK was installed}
  390.   var
  391.     o, n, nhandle : Integer;
  392.  
  393.     procedure EMSdeallocate(EMShandle : Integer);
  394.       {-release the allocated expanded memory}
  395.     begin
  396.       Regs.ah := $45;
  397.       Regs.dx := EMShandle;
  398.       Intr(EMSinterrupt, Regs);
  399.       if Regs.ah <> 0 then begin
  400.         WriteLn('Program error or EMS device not responding');
  401.         WriteLn('EMS memory is now a mess... Please reboot');
  402.         Halt(1);
  403.       end;
  404.     end;                      {EMSdeallocate}
  405.  
  406.   begin
  407.     for n := 1 to EMShandles do begin
  408.       {scan all current handles}
  409.       nhandle := newmap[n].handle;
  410.       if StoredHandles > 0 then begin
  411.         {see if current handle matches one stored by MARK}
  412.         o := 1;
  413.         while (oldmap[o].handle <> nhandle) and (o <= StoredHandles) do
  414.           o := Succ(o);
  415.         {if not, deallocate the current handle}
  416.         if (o > StoredHandles) then
  417.           EMSdeallocate(nhandle);
  418.       end else
  419.         {no handles stored by MARK, deallocate all current handles}
  420.         EMSdeallocate(nhandle);
  421.     end;
  422.   end {releaseEMSblocks} ;
  423.  
  424. begin
  425.  
  426.   WriteLn;
  427.   ReturnCode := 0;
  428.  
  429.   {see if a particular mark is named}
  430.   if ParamCount > 0 then
  431.     markName := ParamStr(1)
  432.   else
  433.     markName := '';
  434.  
  435.   if debug then
  436.     writeln('finding all memory blocks');
  437.  
  438.   {get all allocated memory blocks in normal memory}
  439.   FindTheBlocks;
  440.  
  441.   if debug then
  442.     writeln('finding the marked block');
  443.  
  444.   {find the last one marked with the MARK idstring, and MarkName if specified}
  445.   bottomBlock := FindMark(markID, markName, markOffset);
  446.  
  447.   if debug then
  448.     writeln('marking blocks to release');
  449.  
  450.   {mark those blocks to be released}
  451.   MarkBlocks(bottomBlock);
  452.  
  453.   if debug then
  454.     writeln('copying old interrupt vector table');
  455.  
  456.   {copy the vector table from the MARK resident}
  457.   CopyVectors(bottomBlock, vectorOffset);
  458.  
  459.   if debug then
  460.     writeln('releasing marked memory blocks');
  461.  
  462.   {release normal memory marked for release}
  463.   ReleaseMem;
  464.  
  465.   if debug then
  466.     writeln('dealing with expanded memory');
  467.  
  468.   {see if expanded memory card is installed}
  469.   if EMSpresent then begin
  470.     {see how many EMS handles are currently active}
  471.     EMShandles := EMShandlesActive;
  472.     if EMShandles > MaxHandles then
  473.       WriteLn('EMS process count exceeds capacity of RELEASE')
  474.     else if EMShandles <> 0 then begin
  475.       {see how many handles were active when MARK was installed}
  476.       StoredHandles := GetHandles(bottomBlock, EMScntOffset);
  477.       {get the existing EMS page map}
  478.       GetMem(Map, 4*EMShandles);
  479.       EMSpageMap(Map^);
  480.       {get the stored page map}
  481.       StoredMap := Ptr(Blocks[bottomBlock].psp, EMSmapOffset);
  482.       {compare the two maps and deallocate those not in the stored map}
  483.       ReleaseEMSblocks(StoredMap^, Map^);
  484.     end;
  485.   end;
  486.  
  487.   {DOS will release this program's memory when it exits}
  488.   {write success message}
  489.   Write('RELEASE ', Version, ' - Memory released above last MARK ');
  490.   if markName <> '' then
  491.     WriteLn('(', StUpcase(markName), ')')
  492.   else
  493.     WriteLn;
  494.  
  495.   if ReturnCode <> 0 then
  496.     WriteLn(TrappedBytes:0:0, ' bytes temporarily trapped until batch file completes');
  497.  
  498.   Halt(ReturnCode);
  499. end.
  500.